package feed

import (
	"context"
	"fmt"
	"gitee.com/mootok/mootok/src/models"
	"gitee.com/mootok/mootok/src/storage/database"
	redis2 "gitee.com/mootok/mootok/src/storage/redis"
	"gitee.com/mootok/mootok/src/utils/logging"
	redis3 "github.com/redis/go-redis/v9"
	"github.com/sirupsen/logrus"
)

// PushToFans adds a video to publisher's feed list
func (*Service) PushToFans(context context.Context, video models.Video) error {
	score := float64(video.CreatedAt.Unix())
	logger := logging.LogService("Feed.PushToFans").WithFields(logrus.Fields{
		"video_id": video.ID,
		"score":    score,
	})
	var fansIdList []int

	if err := database.Client.Model(&models.FollowRelation{}).Where("follow_id = ?", video.UserId).
		Pluck("fans_id", &fansIdList).Error; err != nil {
		return fmt.Errorf("fetch fans failed, %v", err)
	}

	logger.Infof("found %d fans", len(fansIdList))

	// AsyncTask
	go asyncStoreAfterPost(context, video.ID, fansIdList, score, logger)

	for i := 0; i < len(fansIdList); i++ {
		redisKey := genFollowVideoFeedListKey(fansIdList[i])
		exist, err := redis2.Client.Exists(context, redisKey).Result()
		if err != nil {
			logger.WithFields(logrus.Fields{
				"fan_id": fansIdList[i],
			}).Warningf("check redis existence failed, %v", err)
			continue
		}
		if exist != 0 {
			if err = redis2.Client.ZAdd(context, redisKey, redis3.Z{
				Score:  score,
				Member: video.ID,
			}).Err(); err != nil {
				logger.WithFields(logrus.Fields{
					"fan_id": fansIdList[i],
				}).Warningf("check redis existence failed, %v", err)
				continue
			}
		}
	}
	return nil
}

// asyncStoreAfterPost save videoId to fans' feed list in db
func asyncStoreAfterPost(_ context.Context, videoId int, fansIdList []int, score float64, logger *logrus.Entry) {
	relations := make([]models.UserVideoFeedRelation, len(fansIdList))
	for index, _ := range fansIdList {
		relations[index].VideoId = videoId
		relations[index].UserId = fansIdList[index]
		relations[index].Score = score
	}
	if err := database.Client.CreateInBatches(relations, 100).Error; err != nil {
		logger.Errorf("save video to feed db failed, %v", err)
		return
	}
}

// RemoveFromFans removes a video from fans' feed list
func RemoveFromFans(context context.Context, video models.Video) error {
	var fansIdList []int
	logger := logging.LogService("Feed.RemoveFromFans").WithFields(logrus.Fields{
		"video_id": video.ID,
	})

	if err := database.Client.Model(&models.FollowRelation{}).Where("follow_id = ?", video.UserId).
		Pluck("fans_id", &fansIdList).Error; err != nil {
		return fmt.Errorf("fetch fans failed, %v", err)
	}

	logger.Infof("found %d fans", len(fansIdList))

	// AsyncTask
	go asyncRemoveAfterDelete(context, video.ID, logger)

	for i := 0; i < len(fansIdList); i++ {
		redisKey := genFollowVideoFeedListKey(fansIdList[i])
		err := redis2.Client.ZRem(context, redisKey, video.ID).Err()
		logger.Errorf("remove from redis failed, %v", err)
		continue
	}
	return nil
}

// asyncStoreAfterPost removes videoId in fans' feed list in db
func asyncRemoveAfterDelete(_ context.Context, videoId int, logger *logrus.Entry) {
	var err error
	err = database.Client.Where("video_id = ?", videoId).
		Delete(&models.UserVideoFeedRelation{}).Error
	if err != nil {
		logger.Errorf("remove UserVideoFeedRelation from db failed %v", err)
		return
	}
}
